package com.shareyi.wox.spring;


import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractXmlApplicationContext;

import com.shareyi.wox.config.ActionBean;
import com.shareyi.wox.core.ObjectFactory;


public class WoxSpringObjectFactory extends ObjectFactory{

	
	private static final Log LOG = LogFactory.getLog(WoxSpringObjectFactory.class);
	protected ApplicationContext appContext;
    protected AutowireCapableBeanFactory autoWiringFactory;
    protected int autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
    private boolean alwaysRespectAutowireStrategy = false;

    public WoxSpringObjectFactory() {
    }

    public WoxSpringObjectFactory(AbstractXmlApplicationContext context){
    	appContext=context;
    	autoWiringFactory=context.getAutowireCapableBeanFactory();
    }
    

    /**
     * Allows for ObjectFactory implementations that support
     * Actions without no-arg constructors.
     *
     * @return true if no-arg constructor is required, false otherwise
     */
    public boolean isNoArgConstructorRequired() {
        return true;
    }


    /**
     * Build an instance of the action class to handle a particular request (eg. web request)
     * @param actionName the name the action configuration is set up with in the configuration
     * @param namespace the namespace the action is configured in
     * @param config the action configuration found in the config for the actionName / namespace
     * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
     * @return instance of the action class to handle a web request
     * @throws Exception
     */
    public Object buildAction(String actionName, String namespace, ActionBean actionBean, Map<String, Object> extraContext) throws Exception {
        return buildBean(actionBean.getClassName(), extraContext);
    }

    /**
     * Build a generic Java object of the given type.
     *
     * @param clazz the type of Object to build
     * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
     */
    public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
    		Object bean;
    	  try {
              // Decide to follow autowire strategy or use the legacy approach which mixes injection strategies
              if (alwaysRespectAutowireStrategy) {
                  // Leave the creation up to Spring
                  bean = autoWiringFactory.createBean(clazz, autowireStrategy, false);
                //  injectApplicationContext(bean);
                //  return injectInternalBeans(bean);
              } else {
                  bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
                  bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());
                  // We don't need to call the init-method since one won't be registered.
                  bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());
                  return autoWireBean(bean, autoWiringFactory);
              }
          } catch (UnsatisfiedDependencyException e) {
              if (LOG.isErrorEnabled())
                  LOG.error("Error building bean", e);
              // Fall back
              return autoWireBean(super.buildBean(clazz, extraContext), autoWiringFactory);
          }
    	  
		return bean;
          
    }
    
    
    
    /**
     * @param bean
     * @param autoWiringFactory
     */
    public Object autoWireBean(Object bean, AutowireCapableBeanFactory autoWiringFactory) {
        if (autoWiringFactory != null) {
            autoWiringFactory.autowireBeanProperties(bean,
                    autowireStrategy, false);
        }
       // injectApplicationContext(bean);
       //        injectInternalBeans(bean);
        return bean;
    }

    /**
     * Sets the autowiring strategy
     *
     * @param autowireStrategy
     */
    public void setAutowireStrategy(int autowireStrategy) {
        switch (autowireStrategy) {
            case AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT:
                if (LOG.isInfoEnabled()) {
                    LOG.info("Setting autowire strategy to autodetect");
                }
                this.autowireStrategy = autowireStrategy;
                break;
            case AutowireCapableBeanFactory.AUTOWIRE_BY_NAME:
                if (LOG.isInfoEnabled()) {
                    LOG.info("Setting autowire strategy to name");
                }
                this.autowireStrategy = autowireStrategy;
                break;
            case AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE:
                if (LOG.isInfoEnabled()) {
                    LOG.info("Setting autowire strategy to type");
                }
                this.autowireStrategy = autowireStrategy;
                break;
            case AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR:
                if (LOG.isInfoEnabled()) {
                    LOG.info("Setting autowire strategy to constructor");
                }
                this.autowireStrategy = autowireStrategy;
                break;
            case AutowireCapableBeanFactory.AUTOWIRE_NO:
                if (LOG.isInfoEnabled()) {
                    LOG.info("Setting autowire strategy to none");
                }
                this.autowireStrategy = autowireStrategy;
                break;
            default:
                throw new IllegalStateException("Invalid autowire type set");
        }
    }

    public int getAutowireStrategy() {
        return autowireStrategy;
    }


    /**
     * Build a generic Java object of the given type.
     *
     * @param className the type of Object to build
     * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
     */
    public Object buildBean(String className, Map<String, Object> extraContext) throws Exception {
    	Object o;
        
        if (appContext.containsBean(className)) {
            o = appContext.getBean(className);
        } else {
            Class beanClazz = getClassInstance(className);
            o = buildBean(beanClazz, extraContext);
        }

        return o;
    }

    public Class getClassInstance(String className) throws ClassNotFoundException{
        Class clazz = null;

        if (appContext.containsBean(className)) {
            clazz = appContext.getBean(className).getClass();
        } else {
            clazz = super.getClassInstance(className);
        }

        return clazz;
    }
    
    

	
	
	

}
